<?php

namespace App\Console\Commands;

use Illuminate\Routing\Console\ControllerMakeCommand as Command;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputOption;

class ControllerMakeCommand extends Command
{
    /**
     * Get the stub file for the generator.
     *
     * @return string
     */
    protected function getStub(): string
    {
        $stub = null;

        if ($type = $this->option('type')) {
            $stub = "/stubs/controller.{$type}.stub";
        } elseif ($this->option('parent')) {
            $stub = '/stubs/controller.nested.stub';
        } elseif ($this->option('model')) {
            $stub = '/stubs/controller.model.stub';
        } elseif ($this->option('invokable')) {
            $stub = '/stubs/controller.invokable.stub';
        } elseif ($this->option('resource')) {
            $stub = '/stubs/controller.stub';
        }

        if ($this->option('service')) {
            if ($this->option('api')) {
                $stub = '/stubs/controller.service.stub';
            } else {
                $stub = '/stubs/controller.service.api.stub';
            }
        }

        if ($this->option('api') && is_null($stub)) {
            $stub = '/stubs/controller.api.stub';
        } elseif ($this->option('api') && !is_null($stub) && !$this->option('invokable')) {
            $stub = str_replace('.stub', '.api.stub', $stub);
        }

        $stub ??= '/stubs/controller.plain.stub';

        return $this->resolveStubPath($stub);
    }

    /**
     * Build the class with the given name.
     *
     * Remove the base controller import if we are already in the base namespace.
     *
     * @param string $name
     * @return string
     */
    protected function buildClass($name): string
    {
        $controllerNamespace = $this->getNamespace($name);

        $replace = [];

        if ($this->option('parent')) {
            $replace = $this->buildParentReplacements();
        }

        if ($this->option('model')) {
            $replace = $this->buildModelReplacements($replace);
        }

        if ($this->option('service')) {
            $replace = $this->buildServiceReplacements($replace);
        }

        $replace["use {$controllerNamespace}\Controller;\n"] = '';

        return str_replace(
            array_keys($replace), array_values($replace), parent::buildClass($name)
        );
    }

    /**
     * Build the service replacement values.
     */
    protected function buildServiceReplacements(array $replace): array
    {
        $serviceClass = $this->parseService($this->option('service'));

        if (!class_exists($serviceClass) && $this->confirm("A {$serviceClass} service does not exist. Do you want to generate it?", true)) {
            $this->call('make:service', ['name' => $serviceClass]);
        }

        $replace = $this->buildFormRequestReplacements($replace, $serviceClass);

        return array_merge($replace, [
            '{{ namespacedService }}' => $serviceClass,
            '{{namespacedService}}' => $serviceClass,
            '{{ service }}' => class_basename($serviceClass),
            '{{service}}' => class_basename($serviceClass),
            '{{ serviceVariable }}' => lcfirst(class_basename($serviceClass)),
            '{{serviceVariable}}' => lcfirst(class_basename($serviceClass)),
        ]);
    }

    /**
     * Get the service class name.
     *
     * @throws InvalidArgumentException
     */
    protected function parseService(string $service): string
    {
        if (preg_match('([^A-Za-z0-9_/\\\\])', $service)) {
            throw new InvalidArgumentException('Model name contains invalid characters.');
        }

        return $this->qualifyService($service);
    }

    /**
     * Qualify the given service class base name.
     */
    protected function qualifyService(string $service): string
    {
        $service = ltrim($service, '\\/');

        $service = str_replace('/', '\\', $service);

        $rootNamespace = $this->rootNamespace();

        if (Str::startsWith($service, $rootNamespace)) {
            return $service;
        }

        return is_dir(app_path('Http/Services'))
            ? $rootNamespace . 'http\\Services\\' . $service
            : $rootNamespace . $service;
    }

    /**
     * Resolve the fully-qualified path to the stub.
     *
     * @param string $stub
     * @return string
     */
    protected function resolveStubPath($stub): string
    {
        $stub1 = parent::resolveStubPath($stub);

        return file_exists($stub1)
            ? $stub1
            : __DIR__ . $stub;
    }

    protected function getOptions(): array
    {
        return array_merge(parent::getOptions(), [
            ['service', null, InputOption::VALUE_OPTIONAL, 'Generate a api controller for the given service.'],
        ]);
    }


}
